home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Datafile PD-CD 1 Issue 2
/
PDCD-1 - Issue 02.iso
/
_utilities
/
utilities
/
003
/
_gs
/
!GS
/
c
/
IDICT
< prev
next >
Wrap
Text File
|
1991-10-26
|
11KB
|
362 lines
/* Copyright (C) 1989, 1990, 1991 Aladdin Enterprises. All rights reserved.
Distributed by Free Software Foundation, Inc.
This file is part of Ghostscript.
Ghostscript is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
to anyone for the consequences of using it or for whether it serves any
particular purpose or works at all, unless he says so in writing. Refer
to the Ghostscript General Public License for full details.
Everyone is granted permission to copy, modify and redistribute
Ghostscript, but only under the conditions described in the Ghostscript
General Public License. A copy of this license is supposed to have been
given to you along with Ghostscript so you can know your rights and
responsibilities. It should be in a file named COPYING. Among other
things, the copyright notice and this notice must be preserved on all
copies. */
/* idict.c */
/* Dictionaries for Ghostscript */
#include "ghost.h"
#include "alloc.h"
#include "errors.h"
#include "name.h"
#include "save.h" /* for value cache in names */
#include "store.h"
#include "dict.h" /* interface definition */
/* Imported procedures */
extern int obj_eq(P2(ref *, ref *));
/*
* A dictionary is a structure of two elements (refs).
* The first element is a t_integer whose value says how many
* entries are occupied (N). The second element is a t_array
* of 2N+2 elements, containing alternating keys and values.
* Unused entries have null as the key. The first entry also
* has null as the key (to avoid a special wrap-around check).
* For now, deleted entries have an executable null as the key:
* this degrades lookup time as entries are inserted and deleted,
* and will probably have to be changed someday.
* The access attributes for the dictionary are stored in
* the contents ref.
*/
struct dict_s {
ref count; /* t_integer */
ref contents; /* t_array */
};
struct pair_s {
ref key;
ref value;
};
typedef struct pair_s pair;
#define pairs(dct) (pair *)((dct)->contents.value.refs)
#define npairs(dct) ((r_size(&(dct)->contents) >> 1) - 1)
/* Define the size of the largest valid dictionary. */
/* This is limited by the size field of the contents ref. */
uint dict_max_size = max_ushort / 2 - 1;
/* Create a dictionary. */
int
dict_create(uint size, ref *pref)
{ uint asize = (size == 0 ? 1 : size) + 1;
dict *pdict =
(dict *)alloc_refs(sizeof(dict) / sizeof(ref), "dict_create");
pair *pp;
if ( pdict == 0 ) return e_VMerror;
pp = (pair *)alloc_refs(asize * 2, "dict_create(pairs)");
if ( pp == 0 )
{ alloc_free((char *)pdict, 1, sizeof(dict), "dict_create");
return e_VMerror;
}
make_tv_new(&pdict->count, t_integer, intval, 0);
make_tasv_new(&pdict->contents, t_array, a_all, asize * 2,
refs, (ref *)pp);
make_tav_new(pref, t_dictionary, a_all, pdict, pdict);
pp = pairs(pdict);
while ( asize-- )
make_null_new(&pp->key),
make_null_new(&pp->value),
pp++;
return 0;
}
/*
* Return a pointer to a ref that holds the access attributes
* for a dictionary.
*/
ref *
dict_access_ref(ref *pdref)
{ return &pdref->value.pdict->contents;
}
/*
* Look up in a stack of dictionaries. Store a pointer to the value slot
* where found, or to the (value) slot for inserting.
* Return 1 if found, 0 if not and there is room for a new entry in
* the top dictionary on the stack, or e_dictfull if the top dictionary
* is full and the key is missing.
* Note that pdbot <= pdtop, and the search starts at pdtop.
*/
int
dict_lookup(ref *pdbot, ref *pdtop, ref *pkey,
ref **ppvalue /* result is stored here */)
{ ref *pdref = pdtop;
uint hash;
int ktype;
name *kpname;
int full = 1; /* gets set to 0 or e_dictfull */
pair *pslot = 0;
/* Compute hash. The only types we bother with are strings, */
/* names, and (unlikely, but worth checking for) integers. */
switch ( r_type(pkey) )
{
case t_name:
kpname = pkey->value.pname;
nh: hash = kpname->index * 40503;
ktype = t_name;
break;
case t_string: /* convert to a name first */
{ ref nref;
int code = name_ref(pkey->value.bytes,
r_size(pkey), &nref, 1);
if ( code < 0 ) return code;
kpname = nref.value.pname;
} goto nh;
case t_integer:
hash = (uint)pkey->value.intval * 40503;
ktype = -1;
break;
default:
hash = r_btype(pkey) * 99; /* yech */
ktype = -1;
}
do
{ dict *pdict = pdref->value.pdict;
uint size = npairs(pdict);
pair *ppbot = pairs(pdict);
register pair *pp; /* probe pointer */
int wrap = 0;
register int etype;
/* Search the dictionary */
#ifdef DEBUG
if ( gs_debug['d'] )
{ extern void debug_print_ref(P1(ref *));
dprintf("[d]");
debug_print_ref(pdref);
dprintf(":");
debug_print_ref(pkey);
dprintf("->");
}
#endif
#ifdef DEBUG
# define print_found()\
if ( gs_debug['d'] )\
{ extern void debug_print_ref(P1(ref *));\
debug_print_ref(&pp->value);\
dprintf("; ");\
}
#else
# define print_found()
#endif
for ( pp = ppbot + (hash % size) + 2; ; )
{ if ( (etype = r_type(&(--pp)->key)) == ktype )
{ /* Fast comparison if both keys are names */
if ( pp->key.value.pname == kpname )
{ *ppvalue = &pp->value;
print_found();
return 1;
}
}
else if ( etype == t_null )
{ if ( r_has_attrs(&pp->key, a_executable) )
{ /* Deleted entry, save the slot. */
if ( pslot == 0 ) pslot = pp;
}
/* We might have hit the dummy entry at */
/* the beginning, in which case we should */
/* wrap around to the end. */
else if ( pp == ppbot ) /* wrap */
{ if ( wrap++ ) /* wrapped twice */
{ if ( full > 0 )
{ if ( pslot != 0 )
break;
full = e_dictfull;
}
goto next_dict;
}
pp += size + 1;
}
else /* key not found */
break;
}
else
{ if ( obj_eq(&pp->key, pkey) )
{ *ppvalue = &pp->value;
print_found();
return 1;
}
}
}
if ( full > 0 )
{ *ppvalue = (pslot != 0 ? &pslot->value : &pp->value);
#ifdef DEBUG
if ( gs_debug['d'] )
dprintf1("0(%lx); ", (ulong)*ppvalue);
#endif
full = 0;
}
next_dict: ;
}
while ( --pdref >= pdbot );
return full;
}
/*
* Enter a key-value pair in a dictionary.
* The caller is responsible for ensuring key is not a null.
* Return 0, e_dictfull, or e_VMerror if the key was a string
* and a VMerror occurred when converting it to a name.
*/
int
dict_put(ref *pdref /* t_dictionary */, ref *pkey, ref *pvalue)
{ ref *pvslot;
if ( dict_find(pdref, pkey, &pvslot) <= 0 ) /* not found */
{ /* Check for overflow */
dict *pdict = pdref->value.pdict;
ref kname;
if ( pdict->count.value.intval == npairs(pdict) )
return e_dictfull;
/* If the key is a string, convert it to a name. */
if ( r_has_type(pkey, t_string) )
{ int code = name_from_string(pkey, &kname);
if ( code < 0 ) return code;
pkey = &kname;
}
ref_save(&pdict->count, "dict_put(count)");
pdict->count.value.intval++;
ref_assign_old(pvslot - 1, pkey, "dict_put(key)"); /* set key of pair */
/* If the key is a name, update its 1-element cache. */
if ( r_has_type(pkey, t_name) )
{ name *pname = pkey->value.pname;
if ( pname->pvalue == pv_no_defn &&
(pdict == systemdict.value.pdict ||
pdict == userdict.value.pdict) &&
/* Only set the cache if we aren't inside */
/* a save. This way, we never have to */
/* undo setting the cache. */
alloc_save_level() == 0
)
{ /* Set the cache */
pname->pvalue = pvslot;
}
else /* The cache is worthless */
pname->pvalue = pv_other;
}
}
ref_assign_old(pvslot, pvalue, "dict_put(value)");
return 0;
}
/* Remove an element from a dictionary. */
int
dict_undef(ref *pdref, ref *pkey)
{ ref *pvslot;
dict *pdict;
if ( dict_find(pdref, pkey, &pvslot) <= 0 )
return e_undefined;
/* Remove the entry from the dictionary. */
pdict = pdref->value.pdict;
pvslot--; /* point to key, not value */
make_null_old(pvslot, "dict_undef(value)");
/* Accumulating deleted entries slows down lookup. */
/* Detect the easy case where we can use an empty entry */
/* rather than a deleted one, namely, when the next entry */
/* in the probe order is empty. */
if ( r_has_type(pvslot - 2, t_null) &&
!r_has_attrs(pvslot - 2, a_executable) &&
pvslot - 2 != pdict->contents.value.refs /* not wraparound */
)
r_set_attrs(pvslot, a_executable); /* mark as deleted */
ref_save(&pdict->count, "dict_undef(count)");
pdict->count.value.intval--;
/* If the key is a name, update its 1-element cache. */
if ( r_has_type(pkey, t_name) )
{ name *pname = pkey->value.pname;
if ( pv_valid(pname->pvalue) &&
(pdict == systemdict.value.pdict ||
pdict == userdict.value.pdict) )
{ /* Clear the cache */
pname->pvalue = pv_no_defn;
}
}
return 0;
}
/* Return the number of elements in a dictionary. */
uint
dict_length(ref *pdref /* t_dictionary */)
{ return (uint)(pdref->value.pdict->count.value.intval);
}
/* Return the capacity of a dictionary. */
uint
dict_maxlength(ref *pdref /* t_dictionary */)
{ return npairs(pdref->value.pdict);
}
/* Copy one dictionary into another. */
int
dict_copy(ref *pdrfrom /* t_dictionary */, ref *pdrto /* t_dictionary */)
{ dict *pdict = pdrfrom->value.pdict;
int count = npairs(pdict) + 1; /* +1 for dummy first entry */
pair *pp = pairs(pdict);
int code;
for ( ; count--; pp++ )
if ( !r_has_type(&pp->key, t_null) )
if ( (code = dict_put(pdrto, &pp->key, &pp->value)) != 0 )
return code;
return 0;
}
/* Resize a dictionary. */
int
dict_resize(ref *pdrfrom, uint new_size)
{ dict *pdict = pdrfrom->value.pdict;
ref drto;
int code;
if ( (code = dict_create(new_size, &drto)) < 0 ) return code;
dict_copy(pdrfrom, &drto); /* can't fail */
/* Free the old dictionary */
alloc_free((char *)pdict->contents.value.refs,
dict_maxlength(pdrfrom), sizeof(pair), "dict_resize(old)");
*pdict = *drto.value.pdict;
/* Free the new dictionary header */
alloc_free((char *)drto.value.pdict,
1, sizeof(dict), "dict_resize(new)");
return 0;
}
/* Prepare to enumerate a dictionary. */
int
dict_first(ref *pdref)
{ return (int)(npairs(pdref->value.pdict) + 1); /* +1 for dummy */
}
/* Enumerate the next element of a dictionary. */
int
dict_next(ref *pdref, int index, ref *eltp /* ref eltp[2] */)
{ pair *pp = pairs(pdref->value.pdict) + index;
while ( pp--, --index >= 0 )
{ if ( !r_has_type(&pp->key, t_null) )
{ eltp[0] = pp->key;
eltp[1] = pp->value;
return index;
}
}
return -1; /* no more elements */
}